#include "textureLoader.h"

void TextureLoader::loadImage(std::string image, GLubyte **dataPointer, int *dataWidth, int *dataHeight)
{
    sf::Image IMG;
    IMG.loadFromFile(image);
    unsigned int width = IMG.getSize().x;
    unsigned int height = IMG.getSize().y;
    if (width == 0 || height == 0)
    {
        std::cout << "Error loading image data!\n";
        *dataPointer = NULL;
        *dataWidth = 0;
        *dataHeight = 0;
        return;
    }
    
    *dataWidth = width;
    *dataHeight = height;

    unsigned int sizeOfData = width*height*4;
    
    GLubyte *dataArray;
    dataArray = new GLubyte[sizeOfData];
    
    const sf::Uint8 *IMGDATA = IMG.getPixelsPtr();

    for (unsigned int a = 0; a < sizeOfData; a++)
    {
        dataArray[a] = IMGDATA[a];    
    }

    *dataPointer = dataArray;
}

GLuint TextureLoader::Load2dTexture(std::string imageName, bool smooth, bool repeat)
{
    GLubyte *textureData;
    int width, height;
    
    loadImage(imageName, &textureData, &width, &height);
    if (width*height == 0)
    {
        return 0;
    }

    GLuint TextureID = 0;
    glGenTextures(1, &TextureID);
    glBindTexture(GL_TEXTURE_2D, TextureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 4);
    glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);

    if (smooth)
        glGenerateMipmap(GL_TEXTURE_2D);
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, smooth ? GL_LINEAR : GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, smooth ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST);
    glBindTexture(GL_TEXTURE_2D, 0);

    delete [] textureData;

    return TextureID;
}

GLuint TextureLoader::Load3dTexture(std::vector<std::string> images, bool smooth, bool repeat)
{
    unsigned int numTextures = images.size();
    if (numTextures == 0)
        return 0;

    std::vector<GLubyte*> texturesData;
    std::vector<Vec2i> texturesSizes;
    texturesData.resize(numTextures);
    texturesSizes.resize(numTextures);

    for (unsigned int i = 0; i < numTextures; i++)
    {    // collect all the data
        loadImage(images[i], &(texturesData[i]), &(texturesSizes[i].x), &(texturesSizes[i].y));
    }    

    int w = texturesSizes[0].x, h = texturesSizes[0].y;

    unsigned int dataIndex = 0;
    int dataSize = w*h*numTextures*4;
    GLubyte *completeData = new GLubyte[dataSize];

    for (unsigned int i = 0; i < numTextures; i++)
    {
        if (texturesSizes[i].x != w || texturesSizes[i].y != h)
        {
            for (unsigned int j = 0; j < texturesData.size(); j++)
            {
                delete [] texturesData[j];
            }
            delete []completeData;
            std::cout << "Error loading textures for 3d texture! Different sizes!\n";
            return 0;
        }

        // copy over the actual data
        for (int k = 0; k < w*h*4; k++)
        {
            completeData[dataIndex] = texturesData[i][k];
            dataIndex += 1;
        }
    }

    GLuint TextureID;
    glGenTextures(1, &TextureID);
    glBindTexture(GL_TEXTURE_3D, TextureID);

    glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, w, h, numTextures, 0, GL_RGBA, GL_UNSIGNED_BYTE, completeData);

    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);

//    if (smooth)
     glGenerateMipmap(GL_TEXTURE_3D);
//    screw mipmapping for 3d textures

    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glBindTexture(GL_TEXTURE_3D, 0);

    for (unsigned int j = 0; j < texturesData.size(); j++)
    {
        delete [] texturesData[j];
    }
    delete []completeData;

    return TextureID;
}

GLuint TextureLoader::Load2DTextureArray(std::vector<std::string> images, bool repeat)
{
    unsigned int numTextures = images.size();
    if (numTextures == 0)
        return 0;

    std::vector<GLubyte*> texturesData;
    std::vector<Vec2i> texturesSizes;
    texturesData.resize(numTextures);
    texturesSizes.resize(numTextures);

    for (unsigned int i = 0; i < numTextures; i++) 
    {
        // Load image data into texturesData and set texturesSize
        loadImage(images[i], &(texturesData[i]), &(texturesSizes[i].x), &(texturesSizes[i].y));
    }    

    int w = texturesSizes[0].x, h = texturesSizes[0].y;

    unsigned int dataIndex = 0;
    int dataSize = w*h*numTextures*4;
    GLubyte *completeData = new GLubyte[dataSize];

    // Verify texture size and copy data into one GLubyte array
    for (unsigned int i = 0; i < numTextures; i++)
    {
        if (texturesSizes[i].x != w || texturesSizes[i].y != h)
        {
            for (unsigned int j = 0; j < texturesData.size(); j++)
            {
                delete [] texturesData[j];
            }
            delete []completeData;
            std::cout << "Error loading textures for 3d texture! Different sizes!\n";
            return 0;
        }

        // Copy over the actual data
        for (int k = 0; k < w*h*4; k++)
        {
            completeData[dataIndex] = texturesData[i][k];
            dataIndex += 1;
        }
    }

    GLuint TextureID;    
    glGenTextures(1, &TextureID);
    glBindTexture(GL_TEXTURE_2D_ARRAY, TextureID);

    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY_EXT, 4);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);       
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
    
    glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, w, h, numTextures,0 , GL_RGBA, GL_UNSIGNED_BYTE, completeData);

    glGenerateMipmap(GL_TEXTURE_2D_ARRAY);

    for (unsigned int j = 0; j < texturesData.size(); j++)
    {
        delete [] texturesData[j];
    }
    delete []completeData;
    glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
    return TextureID;
}